﻿using System;
using System.Linq;
using NUnit.Framework;
using System.Threading;
using XcoAppSpaces.Contracts.Worker;
using XcoAppSpaces.Core;
using XcoAppSpaces.Core.Ports;
using XcoAppSpaces.Contracts.Exceptions;
using XcoAppSpaces.Transport.WCF;
using XcoAppSpaces.Transport.Sockets;
using XcoAppSpaces.Transport.Jabber;
using XcoAppSpaces.Transport.MSMQ;
using XcoAppSpaces.Contracts.Communication;
using XcoAppSpaces.Contracts.WorkerResolve;
using XcoAppSpaces.Contracts.Serialization;
using XcoAppSpaces.Serialization.Json;

namespace test.XcoAppSpaces.Core
{
	#region Test Worker and Contract
	class TestWorkerContract : PortSet<int, string, PortSet<int, string>, bool>
	{
		public AutoResetEvent are = new AutoResetEvent(false);
	}

	class MyTestWorker : TestWorkerContract
	{
		public int intMsg;
		public string strMsg;

		[XcoExclusive]
		public void ProcessInt(int msg)
		{
			this.intMsg = msg;
		}

		[XcoConcurrent]
		public void ProcessString(string msg)
		{
			this.strMsg = msg;
		}

		[XcoConcurrent]
		public void ProcessPort(PortSet<int, string> msg)
		{
			msg.Post(intMsg); //post back last posted integer
		}

		[XcoConcurrent]
		public void ProcessBool(bool msg)
		{
			base.are.Set();
		}
	}

	#endregion

	#region Test Worker and Contract with XcoPublisher
	class PublishTestWorkerContract : PortSet<int, string, Subscribe<int>, Unsubscribe<int>, Subscribe<string>, Unsubscribe<string>>
	{
	}

	class MyPublishTestWorker : PublishTestWorkerContract
	{
		private XcoPublisher<int, string> publisher = new XcoPublisher<int, string>();
		public int intMsg;
		public string strMsg;

		[XcoConcurrent]
		public void ProcessInt(int msg)
		{
			this.intMsg = msg;
			Console.WriteLine("Publishing: " + msg);
			publisher.Publish(msg);
		}

		[XcoConcurrent]
		public void ProcessString(string msg)
		{
			this.strMsg = msg;
			Console.WriteLine("Publishing: " + msg);
			publisher.Publish(msg);
		}
	}
	#endregion


	[TestFixture]
	public class testXcoAppSpace
	{
		private int port1;
		private int port1b;
		private int port2;
		private int port3;
		private string spaceAddress1;
		private string spaceAddress1b;
		private string spaceAddress2;
		private string spaceAddress3;
		private string testWorkerName;

		[SetUp]
		public void Setup()
		{
			port1 = 8001;
			port1b = 9001;
			port2 = 8002;
			port3 = 8003;
			spaceAddress1 = Environment.MachineName + ":" + port1;
			spaceAddress1b = Environment.MachineName + ":" + port1b;
			spaceAddress2 = Environment.MachineName + ":" + port2;
			spaceAddress3 = Environment.MachineName + ":" + port3;
			testWorkerName = "testworker";
		}

		[TearDown]
		public void Teardown()
		{
		}

		[Test]
		public void TestInstantiateLocal()
		{
			using (XcoAppSpace space = new XcoAppSpace())
			{
				Assert.IsNull(space.Address);
			}
		}

		[Test]
		public void TestWorkerLocalAndRemote1()
		{
			TestWorkerLocalAndRemote(spaceAddress1 + "/" + testWorkerName, testWorkerName, false);
		}

		[Test]
		public void TestWorkerLocalAndRemote2()
		{
			TestWorkerLocalAndRemote(spaceAddress1 + "/", null, false);
		}

		[Test]
		public void TestWorkerLocalAndRemote3()
		{
			TestWorkerLocalAndRemote(spaceAddress1, null, false);
		}

		[Test]
		public void TestWorkerLocalAndRemoteWCF()
		{
			TestWorkerLocalAndRemote(/*CreateUri(spaceAddress1, testWorkerName)*/ spaceAddress1 + "/" + testWorkerName, testWorkerName, true);
		}
        [Test]
        public void TestWCFConfig()
        {
            using (XcoAppSpace space = new XcoAppSpace("wcf.port=0"))
            {
                string[] split = space.Address.Split(':');
                Assert.AreNotEqual(int.Parse(split[1]), 0);
            }
        }
        [Test]
        public void TestWCFConfig2()
        {
            using (XcoAppSpace space = XcoAppSpace.Configure.UsingService(new XcoWCFTransportService()).OnPort(0))
            {
                string[] split = space.Address.Split(':');
                Assert.AreNotEqual(int.Parse(split[1]), 0); 
            }
        }

        [Test]
        public void TestMSMQ()
        {
            XcoMSMQTransportService msmq = null;
            string queueName = @".\private$\TestAppSpace2";
            using (XcoAppSpace space = new XcoAppSpace(@"msmq.queuename=" + queueName))
            {
                var msmqTmp = space.Info.TransportServices.First(info => info.Service is XcoMSMQTransportService);
                if (msmqTmp != null && msmqTmp.Service is XcoMSMQTransportService)
                {
                    msmq = (XcoMSMQTransportService)msmqTmp.Service;
                }               
                Assert.AreEqual(space.Address, queueName);
				Assert.AreEqual(msmq.QueueName, queueName);
                
            }
            if (msmq != null)
            {
                msmq.DeleteQueue();
            }
        }

        [Test]
        public void TestMSMQ2()
        {
            string msmqName1 = @".\private$\TestAppSpace";
            string msmqName2 = @".\private$\TestAppSpace2";
            XcoMSMQTransportService msmq1 = new XcoMSMQTransportService();
            XcoMSMQTransportService msmq2 = new XcoMSMQTransportService(msmqName2);

			using (XcoAppSpace space1 = XcoAppSpace.Configure.UsingService(msmq1).WithQueueName(msmqName1),
				space2 = XcoAppSpace.Configure.UsingService(msmq2))
			{

				string msmqWorker = "msmqWorker";
				MyTestWorker localWorker = (MyTestWorker)space1.RunWorker<TestWorkerContract, MyTestWorker>(msmqWorker);
				//   MyTestWorker localWorker = (MyTestWorker)space1.Resolve<TestWorkerContract>(msmqWorker);
				TestWorkerContract remoteWorker = space2.ConnectWorker<TestWorkerContract>(msmqName1 + "/" + msmqWorker);
				remoteWorker.Post("test123");
				Thread.Sleep(1000);
				Assert.AreEqual("test123", localWorker.strMsg);

			}
            msmq1.DeleteQueue();
            msmq2.DeleteQueue();
        }

		[Test]
		public void TestMSMQConfig()
		{
			string queueName = @".\private$\TestAppSpace2";
			using (XcoAppSpace space = new XcoAppSpace(@"msmq.queuename=" + queueName + ";msmq.isdefault=true;msmq.name=msqtest;msmq.disposequeue=true"))
			{
				Assert.AreEqual(1, space.Info.TransportServices.Count(cs => cs.IsDefault && cs.Name == "msqtest"));
				var msmqTmp = space.Info.TransportServices.First(info => info.Service is XcoMSMQTransportService);
				if (msmqTmp != null && msmqTmp.Service is XcoMSMQTransportService)
				{
					XcoMSMQTransportService msmq = (XcoMSMQTransportService)msmqTmp.Service;
					Assert.AreEqual(msmq.QueueName, queueName);
					Assert.AreEqual(msmqTmp.IsDefault, true);
					Assert.AreEqual(msmqTmp.Name, "msqtest");
					Assert.AreEqual(msmq.RemoveQueueOnStop, true);
				}
			}
		}

		[Test]
		public void TestManualWCFConfig()
		{
			string wcfAddr1 = "net.tcp://localhost:8001/XcoAppSpace";
			string wcfAddr2 = "net.tcp://localhost:8002/XcoAppSpace";
			using (XcoAppSpace space1 = XcoAppSpace.Configure.UsingService(new XcoWCFTransportService(wcfAddr1)),
							   space2 = XcoAppSpace.Configure.UsingService(new XcoWCFTransportService(wcfAddr2)))
			{
				MyTestWorker localWorker = (MyTestWorker) space1.RunWorker<TestWorkerContract, MyTestWorker>("myWorker");
				TestWorkerContract remoteWorker = space2.ConnectWorker<TestWorkerContract>("localhost:8001/myWorker");
				remoteWorker.Post("test123");
				Thread.Sleep(1000);
				Assert.AreEqual(localWorker.strMsg, "test123");

			}
		}

		private XcoAppSpace CreateAppSpace(int port, bool wcfComm)
		{
			if (wcfComm)
				return XcoAppSpace.Configure.UsingService(new XcoWCFTransportService()).OnPort(port);
			else
				return XcoAppSpace.Configure.UsingService(new XcoTCPTransportService()).OnPort(port);
		}

		private void TestWorkerLocalAndRemote(string workerAddressStr, string workerName, bool wcfComm)
		{
			using (XcoAppSpace space1 = CreateAppSpace(port1, wcfComm),
							   space2 = CreateAppSpace(port2, wcfComm))
			{
				Assert.IsTrue(spaceAddress1.Equals(space1.Address, StringComparison.OrdinalIgnoreCase));
				Assert.IsTrue(spaceAddress2.Equals(space2.Address, StringComparison.OrdinalIgnoreCase));
				
				//run worker
				space1.RunWorker<TestWorkerContract, MyTestWorker>(workerName);
				MyTestWorker localWorker = (MyTestWorker)space1.Resolve<TestWorkerContract>(workerName);
				
				//test for local worker access
				localWorker.Post(1);
				Thread.Sleep(100);
				Assert.AreEqual(1, localWorker.intMsg);

				//connect to remote worker
				TestWorkerContract remoteWorker = //(workerUri != null ? 
					//space2.ConnectWorker<TestWorkerContract>(workerUri) : 
					space2.ConnectWorker<TestWorkerContract>(workerAddressStr); //);
				TestWorkerContract remoteWorker2 = space2.Resolve<TestWorkerContract>();
				Assert.AreSame(remoteWorker, remoteWorker2);

				//tests for remote worker access and remote port
				remoteWorker.Post(2);
				remoteWorker.Post("test");
				Thread.Sleep(100);
				PortSet<int, string> responsePort = new PortSet<int, string>();
				remoteWorker.Post(responsePort);
				Thread.Sleep(500);
				Assert.AreEqual(2, localWorker.intMsg);
				Assert.AreEqual("test", localWorker.strMsg);
				Assert.AreEqual(1, responsePort.P0.ItemCount);
				Assert.AreEqual(0, responsePort.P1.ItemCount);
				int postedInt = responsePort.P0.Receive().Result;
				Assert.AreEqual(2, postedInt);
			}
		}

		[Test]
		public void TestWorkerLocalOnly()
		{
			using (XcoAppSpace space1 = new XcoAppSpace())
			{
				//run worker
				space1.RunWorker<TestWorkerContract, MyTestWorker>(testWorkerName);
				MyTestWorker localWorker = (MyTestWorker)space1.Resolve<TestWorkerContract>(testWorkerName);

				//test for local worker access
				localWorker.Post(1);
				Thread.Sleep(100);
				Assert.AreEqual(1, localWorker.intMsg);

				//try to connect to remote worker
				try
				{
					TestWorkerContract remoteWorker = space1.ConnectWorker<TestWorkerContract>(spaceAddress2 + "/" + testWorkerName);
					Assert.Fail("XcoCommunicationException should have been thrown!");
				}
				catch (XcoCommunicationException)
				{
				}
				//space address should be null
				Assert.IsNull(space1.Address);
			}
		}

		[Test]
		public void TestWorkerWithSubscribeLocal()
		{
			TestWorkerWithSubscribe(false);
		}

		[Test]
		public void TestWorkerWithSubscribeRemote()
		{
			TestWorkerWithSubscribe(true);
		}


		public void TestWorkerWithSubscribe(bool remote)
		{
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=" + port1), space2 = new XcoAppSpace("tcp.port=" + port2))
			{
				//run worker
				space1.RunWorker<PublishTestWorkerContract, MyPublishTestWorker>(testWorkerName);
				PublishTestWorkerContract worker = null;
				if (remote)
					worker = space2.ConnectWorker<PublishTestWorkerContract>(spaceAddress1 + "/" + testWorkerName);
				else
					worker = space1.Resolve<PublishTestWorkerContract>(testWorkerName);
				
				//subscribe to events
				Port<int> eventPortInt = new Port<int>();
				Port<string> eventPortStr = new Port<string>();
				worker.Post(new Subscribe<int>(eventPortInt));
				worker.Post(new Subscribe<string>(eventPortStr));
				Thread.Sleep(500);

				//post something
				worker.Post(123);
				worker.Post("test");
				Thread.Sleep(500);

				//events should have been received
				Assert.AreEqual(1, eventPortInt.ItemCount);
				int postedInt = eventPortInt.Receive(1000).Result;
				Assert.AreEqual(123, postedInt);
				Assert.AreEqual(1, eventPortStr.ItemCount);
				string postedStr = eventPortStr.Receive(1000).Result;
				Assert.AreEqual("test", postedStr);

				//unsubscribe from events
				worker.Post(new Unsubscribe<int>(eventPortInt));
				worker.Post(new Unsubscribe<string>(eventPortStr));
				Thread.Sleep(100);

				//post something again
				worker.Post(234);
				worker.Post("test2");
				Thread.Sleep(100);

				//no events should have been received
				Assert.AreEqual(0, eventPortInt.ItemCount);
				Assert.AreEqual(0, eventPortStr.ItemCount);
			}
		}

		[Test]
		public void TestWorkerPreInstantiated1()
		{
			using (XcoAppSpace space = new XcoAppSpace())
			{
				var workerPre = new MyWorker();
				//space.Register<WorkerContract, MyWorker>();
				WorkerContract worker = space.RunWorker<WorkerContract>(workerPre);
				Assert.IsNotNull(worker);
				Assert.AreSame(workerPre, worker);
				Assert.AreSame(worker, space.Resolve<WorkerContract>());
			}
		}

		[Test]
		public void TestWorker2PreInstantiated2()
		{
			using (XcoAppSpace space = new XcoAppSpace())
			{
				var workerPre = new MyWorker();
				//space.Register<WorkerContract, MyWorker>(testWorkerName);
				WorkerContract worker = space.RunWorker<WorkerContract>(workerPre, testWorkerName);
				Assert.IsNotNull(worker);
				Assert.AreSame(workerPre, worker);
				Assert.AreSame(worker, space.Resolve<WorkerContract>(testWorkerName));
			}
		}

		[Test]
		public void TestWorkerStop()
		{
			using (XcoAppSpace space = new XcoAppSpace())
			{
				//register worker
				//space.Register<WorkerContract, MyWorker>(testWorkerName);
				WorkerContract worker = space.RunWorker<WorkerContract, MyWorker>(testWorkerName);
				Assert.IsNotNull(worker);
				Assert.IsInstanceOf(typeof(MyWorker), worker);
				MyWorker mw = (MyWorker)worker;
				Assert.IsNotNull(space.Resolve<WorkerContract>(testWorkerName));
				//Assert.IsTrue(UnityContainerExtensions.CanResolveWorkerInstance(space.DefaultDIContainer, typeof(WorkerContract), testWorkerName));
				worker.Post("test");
				Thread.Sleep(100);
				Assert.AreEqual("test", mw.strMsg);

				//dispose worker
				space.StopWorker(worker);
				//WorkerContract newWorker = space.Resolve<WorkerContract>(testWorkerName);
				//Assert.AreNotSame(worker, newWorker);
				Assert.IsNull(space.Resolve<WorkerContract>(testWorkerName));
				//Assert.IsFalse(UnityContainerExtensions.CanResolveWorkerInstance(space.DefaultDIContainer, typeof(WorkerContract), testWorkerName));
				worker.Post("test2");
				Thread.Sleep(100);
				Assert.AreEqual("test", mw.strMsg); //ccr receiver tasks should have been unregistered
			}
		}

		[Test]
		public void TestWorkerStop2()
		{
			using (XcoAppSpace space = new XcoAppSpace())
			{
				//register worker
				//space.Register<WorkerContract, MyWorker>(testWorkerName);
				WorkerContract worker = space.RunWorker<WorkerContract, MyWorker>(testWorkerName);
				Assert.IsNotNull(worker);
				Assert.IsInstanceOf(typeof(MyWorker), worker);
				MyWorker mw = (MyWorker)worker;
				Assert.IsNotNull(space.Resolve<WorkerContract>(testWorkerName));
				//Assert.IsTrue(UnityContainerExtensions.CanResolveWorkerInstance(space.DefaultDIContainer, typeof(WorkerContract), testWorkerName));
				worker.Post("test");
				Thread.Sleep(100);
				Assert.AreEqual("test", mw.strMsg);

				//dispose worker
				space.StopWorker<WorkerContract>(testWorkerName);
				//WorkerContract newWorker = space.Resolve<WorkerContract>(testWorkerName);
				//Assert.AreNotSame(worker, newWorker);
				Assert.IsNull(space.Resolve<WorkerContract>(testWorkerName));
				//Assert.IsFalse(UnityContainerExtensions.CanResolveWorkerInstance(space.DefaultDIContainer, typeof(WorkerContract), testWorkerName));
				worker.Post("test2");
				Thread.Sleep(100);
				Assert.AreEqual("test", mw.strMsg); //ccr receiver tasks should have been unregistered
			}
		}

		[Test]
		public void TestWorkerStop3()
		{
			using (XcoAppSpace space = new XcoAppSpace())
			{
				//register worker
				//space.Register<WorkerContract, MyWorker>();
				WorkerContract worker = space.RunWorker<WorkerContract, MyWorker>();
				Assert.IsNotNull(worker);
				Assert.IsInstanceOf(typeof(MyWorker), worker);
				MyWorker mw = (MyWorker)worker;
				Assert.IsNotNull(space.Resolve<WorkerContract>());
				//Assert.IsTrue(UnityContainerExtensions.CanResolveWorkerInstance(space.DefaultDIContainer, typeof(WorkerContract), (string) null));
				worker.Post("test");
				Thread.Sleep(100);
				Assert.AreEqual("test", mw.strMsg);

				//dispose worker
				space.StopWorker<WorkerContract>();
				//WorkerContract newWorker = space.Resolve<WorkerContract>();
				//Assert.AreNotSame(worker, newWorker);
				Assert.IsNull(space.Resolve<WorkerContract>(testWorkerName));
				//Assert.IsFalse(UnityContainerExtensions.CanResolveWorkerInstance(space.DefaultDIContainer, typeof(WorkerContract), (string) null));
				worker.Post("test2");
				Thread.Sleep(100);
				Assert.AreEqual("test", mw.strMsg); //ccr receiver tasks should have been unregistered
			}
		}

		[Test]
		public void TestWorkerStop4()
		{
			//dispose with remote communication
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=" + port1),
				space2 = new XcoAppSpace("tcp.port=" + port2))
			{
				//register worker
				WorkerContract worker = space1.RunWorker<WorkerContract, MyWorker>();
				Assert.IsNotNull(space1.Resolve<WorkerContract>());
				//Assert.IsTrue(UnityContainerExtensions.CanResolveWorkerInstance(space1.DefaultDIContainer, typeof(WorkerContract), (string) null));
				//connect to worker
				WorkerContract remoteWorker = space2.ConnectWorker<WorkerContract>(spaceAddress1);
				Assert.IsNotNull(remoteWorker);
				//dispose worker
				space1.StopWorker<WorkerContract>();
				Assert.IsNull(space1.Resolve<WorkerContract>(testWorkerName));
				//Assert.IsFalse(UnityContainerExtensions.CanResolveWorkerInstance(space1.DefaultDIContainer, typeof(WorkerContract), (string) null));
				//try again to connect to worker
				try
				{
					remoteWorker = space2.ConnectWorker<WorkerContract>(spaceAddress1);
					Assert.Fail();
				}
				catch (XcoCommunicationException)
				{
				}
			}
		}

		[Test]
		public void TestWorkerUnpublished()
		{
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=" + port1),
				space2 = new XcoAppSpace("tcp.port=" + port2))
			{
				//register worker
                WorkerContract worker = space1.RunWorker<WorkerContract, MyWorker>(PublishMode.Unpublished, null);
				//try to connect to worker
				try
				{
					WorkerContract remoteWorker = space2.ConnectWorker<WorkerContract>(spaceAddress1);
					Assert.Fail();
				}
				catch (XcoCommunicationException)
				{
				}
			}
		}

		[Test]
		public void TestWorkerProxy()
		{
			//space communicates over the published worker proxy of another space
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=" + port1),
				space2 = new XcoAppSpace("tcp.port=" + port2),
				space3 = new XcoAppSpace("tcp.port=" + port3))
			{
				WorkerContract worker = space1.RunWorker<WorkerContract, MyWorker>();
				WorkerContract workerProxy1 = space2.ConnectWorker<WorkerContract>(PublishMode.Published, spaceAddress1);
				WorkerContract workerProxy2 = space3.ConnectWorker<WorkerContract>(spaceAddress2);
				//test with message
				workerProxy2.Post(123);
				Thread.Sleep(1000);
				MyWorker mw = (MyWorker)worker;
				Assert.AreEqual(123, mw.intMsg);
			}
		}

		[Test]
		public void TestWorkerProxyUnpublished()
		{
			//by default worker proxies should not be published
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=" + port1),
				space2 = new XcoAppSpace("tcp.port=" + port2),
				space3 = new XcoAppSpace("tcp.port=" + port3))
			{
				WorkerContract worker = space1.RunWorker<WorkerContract, MyWorker>();
				WorkerContract workerProxy1 = space2.ConnectWorker<WorkerContract>(spaceAddress1);
				try
				{
					WorkerContract workerProxy2 = space3.ConnectWorker<WorkerContract>(spaceAddress2);
					Assert.Fail();
				}
				catch (XcoCommunicationException)
				{
				}
			}
		}


		[Test]
		public void TestWorkerHostWithMultipleCommServices()
		{
			using (
				XcoAppSpace space1 = XcoAppSpace.Configure
					.UsingService(new XcoTCPTransportService()).OnPort(port1).WithName("tcp").AsDefault().WithConnectionLifetime(TimeSpan.FromSeconds(60))
					.UsingService(new XcoWCFTransportService()).OnPort(port1b).WithName("wcf"),
				space2tcp = XcoAppSpace.Configure.UsingService(new XcoTCPTransportService()).OnPort(port2),
				space3wcf = XcoAppSpace.Configure.UsingService(new XcoWCFTransportService()).OnPort(port3))
			{
				//run worker
				space1.RunWorker<TestWorkerContract, MyTestWorker>(testWorkerName);
				MyTestWorker localWorker = (MyTestWorker)space1.Resolve<TestWorkerContract>(testWorkerName);

				//remote worker over tcp
				TestWorkerContract remoteWorkerTcp = space2tcp.ConnectWorker<TestWorkerContract>(spaceAddress1 + "/" + testWorkerName);
				//tests for remote worker access and remote port
				remoteWorkerTcp.Post(1);
				remoteWorkerTcp.Post("testTcp");
				Thread.Sleep(100);
				PortSet<int, string> responsePort = new PortSet<int, string>();
				remoteWorkerTcp.Post(responsePort);
				Thread.Sleep(100);
				Assert.AreEqual(1, localWorker.intMsg);
				Assert.AreEqual("testTcp", localWorker.strMsg);
				Assert.AreEqual(1, responsePort.P0.ItemCount);
				Assert.AreEqual(0, responsePort.P1.ItemCount);
				int postedInt = responsePort.P0.Receive().Result;
				Assert.AreEqual(1, postedInt);

				//remote worker over wcf
				TestWorkerContract remoteWorkerWcf = space3wcf.ConnectWorker<TestWorkerContract>(spaceAddress1b + "/" + testWorkerName);
				//tests for remote worker access and remote port
				remoteWorkerWcf.Post(2);
				remoteWorkerWcf.Post("testWcf");
				Thread.Sleep(100);
				responsePort = new PortSet<int, string>();
				remoteWorkerWcf.Post(responsePort);
				Thread.Sleep(100);
				Assert.AreEqual(2, localWorker.intMsg);
				Assert.AreEqual("testWcf", localWorker.strMsg);
				Assert.AreEqual(1, responsePort.P0.ItemCount);
				Assert.AreEqual(0, responsePort.P1.ItemCount);
				postedInt = responsePort.P0.Receive().Result;
				Assert.AreEqual(2, postedInt);
			}
		}

		[Test]
		public void TestWorkerConnectWithMultipleCommServices()
		{
			using (
				XcoAppSpace space1 = XcoAppSpace.Configure
					.UsingService<XcoTCPTransportService>().OnPort(port1).WithName("tcp").AsDefault()
					.UsingService<XcoWCFTransportService>().OnPort(port1b).WithName("wcf"),
				space2tcp = XcoAppSpace.Configure.UsingService<XcoTCPTransportService>().OnPort(port2),
				space3wcf = XcoAppSpace.Configure.UsingService<XcoWCFTransportService>().OnPort(port3))
			{
				//run workers at space 2 and 3
				MyTestWorker localWorkerTcp = (MyTestWorker)space2tcp.RunWorker<TestWorkerContract, MyTestWorker>("1");
				MyTestWorker localWorkerWcf = (MyTestWorker)space3wcf.RunWorker<TestWorkerContract, MyTestWorker>("2");
				
				//connect to remote worker at space 2 over tcp (default)
				TestWorkerContract remoteWorker = space1.ConnectWorker<TestWorkerContract>(spaceAddress2 + "/1", "1");
				//tests for remote worker access and remote port
				remoteWorker.Post(1);
				remoteWorker.Post("testTcp");
				Thread.Sleep(100);
				PortSet<int, string> responsePort = new PortSet<int, string>();
				remoteWorker.Post(responsePort);
				Thread.Sleep(100);
				Assert.AreEqual(1, localWorkerTcp.intMsg);
				Assert.AreEqual("testTcp", localWorkerTcp.strMsg);
				Assert.AreEqual(1, responsePort.P0.ItemCount);
				Assert.AreEqual(0, responsePort.P1.ItemCount);
				int postedInt = responsePort.P0.Receive().Result;
				Assert.AreEqual(1, postedInt);

				//remote worker at space 3 over wcf
				remoteWorker = space1.ConnectWorker<TestWorkerContract>("wcf", spaceAddress3 + "/2", "2");
				//tests for remote worker access and remote port
				remoteWorker.Post(2);
				remoteWorker.Post("testWcf");
				Thread.Sleep(100);
				responsePort = new PortSet<int, string>();
				remoteWorker.Post(responsePort);
				Thread.Sleep(100);
				Assert.AreEqual(2, localWorkerWcf.intMsg);
				Assert.AreEqual("testWcf", localWorkerWcf.strMsg);
				Assert.AreEqual(1, responsePort.P0.ItemCount);
				Assert.AreEqual(0, responsePort.P1.ItemCount);
				postedInt = responsePort.P0.Receive().Result;
				Assert.AreEqual(2, postedInt);
			}
		}

		[Test]
		[ExpectedException(typeof(XcoCommunicationException))]
		public void TestWithNotExistingCommService()
		{
			using (
				XcoAppSpace space1 = XcoAppSpace.Configure
					.UsingService(new XcoTCPTransportService()).OnPort(port1).WithName("tcp").AsDefault()
					.UsingService(new XcoWCFTransportService()).OnPort(port1b).WithName("wcf"),
				space2 = XcoAppSpace.Configure.UsingService(new XcoTCPTransportService()).OnPort(port2))
			{
				//run worker
				space1.RunWorker<TestWorkerContract, MyTestWorker>(testWorkerName);
				MyTestWorker localWorker = (MyTestWorker)space1.Resolve<TestWorkerContract>(testWorkerName);

				//try to connect to remote worker
				TestWorkerContract remoteWorkerTcp = space2.ConnectWorker<TestWorkerContract>("unknownCommService", spaceAddress1 + "/" + testWorkerName);
			}
		}

		[Test]
		public void TestSpaceInfo()
		{
			XcoTCPTransportService comm1 = new XcoTCPTransportService();
			XcoWCFTransportService comm2 = new XcoWCFTransportService();
			using (
				XcoAppSpace space1 = XcoAppSpace.Configure
					.UsingService(comm1).OnPort(port1).WithName("tcp").AsDefault()
					.UsingService(comm2).OnPort(port1b).WithName("wcf"),
				space2 = XcoAppSpace.Configure.UsingService(new XcoTCPTransportService()).OnPort(port2))
			{
				//check communication services information
				Assert.AreEqual(2, space1.Info.TransportServices.Count());
				Assert.AreEqual(1, space1.Info.TransportServices.Count(cs => cs.IsDefault && cs.Name == "tcp" && cs.Service == comm1));
				Assert.AreEqual(1, space1.Info.TransportServices.Count(cs => !cs.IsDefault && cs.Name == "wcf" && cs.Service == comm2));
				
				//run some workers
				int initialWorkerCount = space1.Info.Workers.Count();
				TestWorkerContract worker = space1.RunWorker<TestWorkerContract, MyTestWorker>("w1");
				PublishTestWorkerContract worker2 = space1.RunWorker<PublishTestWorkerContract, MyPublishTestWorker>("w2");
				space2.RunWorker<TestWorkerContract, MyTestWorker>("w3");
				
				//check number of connect calls (must increase when ConnectWorker is called, must not increase when messages are posted)
				Assert.AreEqual(0, space2.Info.Workers.First(w => w.Worker is TestWorkerContract).NumberOfConnections);
				TestWorkerContract worker3 = space1.ConnectWorker<TestWorkerContract>(spaceAddress2 + "/w3", "w3");
				Assert.AreEqual(1, space2.Info.Workers.First(w => w.Worker is TestWorkerContract).NumberOfConnections);
				worker3.Post("test");
				worker3.Post(123);
				Thread.Sleep(1000);
				Assert.AreEqual(1, space2.Info.Workers.First(w => w.Worker is TestWorkerContract).NumberOfConnections);
				
				//check worker information (3 registered workers + resolve worker)
				Assert.AreEqual(initialWorkerCount + 3, space1.Info.Workers.Count());
				Assert.AreEqual(1, space1.Info.Workers.Count(w => w.Name == "w1" && w.IsPublished && !w.IsRemote && w.Worker == worker && w.RemoteAddress == null && w.TransportServiceName == null));
				Assert.AreEqual(1, space1.Info.Workers.Count(w => w.Name == "w2" && w.IsPublished && !w.IsRemote && w.Worker == worker2 && w.RemoteAddress == null && w.TransportServiceName == null));
				Assert.AreEqual(1, space1.Info.Workers.Count(w => w.Name == "w3" && !w.IsPublished && w.IsRemote && w.Worker == worker3 && w.RemoteAddress == (spaceAddress2 + "/w3") && w.TransportServiceName == "tcp"));

				//check port information
				Assert.IsFalse(worker.P0.GetPortInfo().IsRemotePort);
				Assert.IsTrue(worker3.P0.GetPortInfo().IsRemotePort);
				Assert.AreEqual(spaceAddress2, worker3.P0.GetPortInfo().RemoteAddress);
				Assert.AreEqual("tcp", worker3.P0.GetPortInfo().TransportServiceName);
				//Assert.AreSame(comm1, worker3.P0.GetPortInfo().CommService.Service);
				//Assert.IsTrue(worker3.P0.GetPortInfo().CommService.IsDefault);

				//dispose workers and check if info was removed
				space1.StopWorker(worker);
				Assert.AreEqual(0, space1.Info.Workers.Count(w => w.Worker == worker));
				space1.StopWorker(worker3);
				Assert.AreEqual(0, space1.Info.Workers.Count(w => w.Worker == worker3));
				Assert.AreEqual(2, space1.Info.Workers.Count());
			}
		}

		[Test]
		public void TestInstantiateWithConfigString_Tcp()
		{
			using (XcoAppSpace space = new XcoAppSpace("tcp.port=9000;tcp.name=tcpservicename"))
			{
				Assert.AreEqual(1, space.Info.TransportServices.Count());
				var commServiceInfo = space.Info.TransportServices.First();
				Assert.AreEqual(9000, ((XcoTCPTransportService)commServiceInfo.Service).Port);
				Assert.AreEqual("tcpservicename", commServiceInfo.Name);
				Assert.IsTrue(commServiceInfo.IsDefault);
			}
		}

		[Test]
		public void TestInstantiateWithConfigString_TcpAndWcf()
		{
			using (XcoAppSpace space = new XcoAppSpace("tcp.name=tcpservicename;tcp.port=9000;wcf.port=9001;wcf.name=wcfservicename;wcf.isdefault=true"))
			{
				Assert.AreEqual(2, space.Info.TransportServices.Count());
				var commServiceInfoTcp = space.Info.TransportServices.First(info => info.Service is XcoTCPTransportService);
				Assert.AreEqual(9000, ((XcoTCPTransportService)commServiceInfoTcp.Service).Port);
				Assert.AreEqual("tcpservicename", commServiceInfoTcp.Name);
				Assert.IsFalse(commServiceInfoTcp.IsDefault);
				var commServiceInfoWcf = space.Info.TransportServices.First(info => info.Service is XcoWCFTransportService);
				Assert.AreEqual(9001, ((XcoWCFTransportService)commServiceInfoWcf.Service).Port);
				Assert.AreEqual("wcfservicename", commServiceInfoWcf.Name);
				Assert.IsTrue(commServiceInfoWcf.IsDefault);
			}
		}

		[Test]
		public void TestInstantiateWithConfigString_JsonSerializer()
		{
			using (var space = new XcoAppSpace("tcp.port=9000;serializer=json"))
			{
				var serializer = space.Info.ServiceRegistry.Resolve<IXcoSerializer>();
				Assert.That(serializer, Is.InstanceOf<XcoJsonSerializer>());
			}
		}

		[Test]
		[ExpectedException(typeof(XcoConfigurationException))]
		public void TestInstantiateWithConfigString_Serializer_Error()
		{
			using (var space = new XcoAppSpace("tcp.port=9000;serializer=unknown"))
			{
			}
		}

		[Test]
		public void TestInstantiateWithConfigString_Jabber()
		{
			using (XcoAppSpace space = new XcoAppSpace("jabber.jid=xco001@jabber.org/res001;jabber.password=xco001"))
			{
				Assert.AreEqual(1, space.Info.TransportServices.Count());
				var commServiceInfo = space.Info.TransportServices.First(info => info.Service is XcoJabberTransportService);
				Assert.IsTrue(commServiceInfo.IsDefault);
			}
		}

		[Test]
		[ExpectedException(typeof(XcoConfigurationException))]
		public void TestInstantiateWithConfigString_Error()
		{
			//invalid config value for log.level
			using (XcoAppSpace space = new XcoAppSpace("tcp.port=bla"))
			{
			}
		}

		[Test]
		public void TestInstantiateWithConfigString_Empty()
		{
			//should start without remote communication
			using (XcoAppSpace space = new XcoAppSpace(""))
			{
				Assert.AreEqual(0, space.Info.TransportServices.Count());
			}
		}

		[Test]
		[ExpectedException(typeof(XcoConfigurationException))]
		public void TestInstantiateWithConfigString_Error2()
		{
			//invalid config value for tcp.port
			using (XcoAppSpace space = new XcoAppSpace("tcp.port=-1"))
			{
			}
		}

		[Test]
		public void TestInstantiateWithConfigStringAndFluentConfig()
		{
			XcoTCPTransportService commService = new XcoTCPTransportService(port1); 
			using (XcoAppSpace space = XcoAppSpace.ConfigureWithConfigString("wcf.port=0").UsingService(commService).WithName("tcp"))
			{
				Assert.AreEqual(2, space.Info.TransportServices.Count());
				Assert.That(space.Info.TransportServices.First().Service, Is.InstanceOf<XcoWCFTransportService>());
				Assert.AreEqual(commService, space.Info.TransportServices.Last().Service);
			}
		}

		[Test]
		[ExpectedException(typeof(XcoWorkerException))]
		public void TestRunWorker_SameWorkerCannotBeRunTwice()
		{
			using (XcoAppSpace space1 = new XcoAppSpace())
			{
				space1.RunWorker<TestWorkerContract, MyTestWorker>(testWorkerName);
				space1.RunWorker<TestWorkerContract, MyTestWorker>(testWorkerName);
			}
		}

		[Test]
		public void TestRunWorker_WorkersWithDifferentNamesCanBeRun()
		{
			using (XcoAppSpace space1 = new XcoAppSpace())
			{
				space1.RunWorker<TestWorkerContract, MyTestWorker>();
				space1.RunWorker<TestWorkerContract, MyTestWorker>("worker1");
				space1.RunWorker<TestWorkerContract, MyTestWorker>("worker2");
			}
		}

		[Test]
		public void TestCommunicationEvents()
		{
			//Arrange
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=" + port1),
							   space2 = new XcoAppSpace("tcp.port=" + port2))
			{
				space2.Run<string>(msg => Console.WriteLine("Message processed"));
				var remotePort = space1.Connect<string>(spaceAddress2);
				MessageEventArgs sendingEvent = null;
				MessageEventArgs receivedEvent = null;
				space1.Info.CommunicationEvents.OnSendingMessage += e =>
				{
					Console.WriteLine("Space 1 sending message. Size: " + e.Message.Content.Length + ", Info: " + e.Message.ContentInfo);
					sendingEvent = e;
				};
				space2.Info.CommunicationEvents.OnMessageReceived += e =>
				{
					Console.WriteLine("Space 2 received message. Size: " + e.Message.Content.Length + ", Info: " + e.Message.ContentInfo);
					receivedEvent = e;
				};

				//Act
				remotePort.Post("test");
				
				//Assert
				SpinWait.SpinUntil(() => receivedEvent != null, 2000);
				Assert.IsNotNull(sendingEvent);
				Assert.IsTrue(sendingEvent.Address.Equals(spaceAddress2, StringComparison.OrdinalIgnoreCase));
				Assert.IsNotNull(receivedEvent);
				Assert.IsTrue(receivedEvent.Address.Equals(spaceAddress1, StringComparison.OrdinalIgnoreCase));
			}
		}

		[Test]
		public void TestWorkerResolveEvent_WorkerExists()
		{
			//Arrange
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=" + port1),
							   space2 = new XcoAppSpace("tcp.port=" + port2))
			{
				space2.Run<string>(msg => Console.WriteLine("Message processed"));
				WorkerResolveEventArgs evt = null;
				space2.Info.CommunicationEvents.OnWorkerResolved += e =>
				{
					Console.WriteLine("Worker has been resolved. Identifier: " + e.WorkerIdentifier + ", Result: " + e.Result);
					evt = e;
				};
				
				//Act
				space1.Connect<string>(spaceAddress2);

				//Assert
				Assert.IsNotNull(evt);
				Assert.AreEqual(ResolveStatus.Ok, evt.Result);
				Assert.AreEqual("port<string>", evt.WorkerIdentifier);
			}
		}

		[Test]
		public void TestWorkerResolveEvent_WorkerDoesntExist()
		{
			//Arrange
			using (XcoAppSpace space1 = new XcoAppSpace("tcp.port=" + port1),
							   space2 = new XcoAppSpace("tcp.port=" + port2))
			{
				space2.Run<string>(msg => Console.WriteLine("Message processed"));
				WorkerResolveEventArgs evt = null;
				space2.Info.CommunicationEvents.OnWorkerResolved += e =>
				{
					Console.WriteLine("Worker has been resolved. Identifier: " + e.WorkerIdentifier + ", Result: " + e.Result);
					evt = e;
				};

				//Act
				try
				{
					space1.Connect<int>(spaceAddress2);
				}
				catch (XcoCommunicationException) { }

				//Assert
				Assert.IsNotNull(evt);
				Assert.AreEqual(ResolveStatus.WorkerNotFound, evt.Result);
				Assert.AreEqual("port<int>", evt.WorkerIdentifier);
			}
		}
	}
}
